package com.framework.util;

import com.sun.org.apache.bcel.internal.util.ClassSet;
import com.xiaoleilu.hutool.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Created by
 *
 * @author dw
 * @date 2018/01/22
 */
public class ClassUtil {
    private static final Logger log = LoggerFactory.getLogger(ClassUtil.class);

    public static ClassLoader getClassLoader() {
        return Thread.currentThread().getContextClassLoader();
    }

    public static Class<?> loadClass(String className, boolean isInitialized) {
        Class<?> cla = null;
        try {
            cla = Class.forName(className, isInitialized, getClassLoader());
        } catch (ClassNotFoundException e) {
            log.error(e.getMessage(), e);
            e.printStackTrace();
        }
        return cla;
    }

    public static Class<?> loadClass(String className) {
        Class<?> cla = null;
        try {
            cla = Class.forName(className, false, getClassLoader());
        } catch (ClassNotFoundException e) {
            log.error(e.getMessage(), e);
            e.printStackTrace();
        }
        return cla;
    }

    public static Set<Class<?>> getClassSet(String packageName) {
        Set<Class<?>> set = new HashSet<Class<?>>();
        try {
            Enumeration<URL> urls = getClassLoader().getResources(packageName.replaceAll(",", "/"));
            while (urls.hasMoreElements()) {
                String protocol = urls.nextElement().getProtocol();
                if (protocol.equals("file")) {
                    String packagePaht = urls.nextElement().getPath().replaceAll("%20", " ");
                    addclass(set, packagePaht, packageName);
                } else if (protocol.equals("jar")) {
                    JarURLConnection jarURLConnection = (JarURLConnection) urls.nextElement().openConnection();
                    if (null != jarURLConnection) {
                        JarFile jarFile = jarURLConnection.getJarFile();
                        if (null != jarFile) {
                            Enumeration<JarEntry> jarEntrys = jarFile.entries();
                            while (jarEntrys.hasMoreElements()) {
                                JarEntry jarEntry = jarEntrys.nextElement();
                                String jarEntryName = jarEntry.getName();
                                if (jarEntryName.endsWith(".class")) {
                                    String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).
                                            replaceAll("/", ".");
                                    addclass(set, className);
                                }
                            }
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return set;
    }

    private static void addclass(Set<Class<?>> set, String packagePath, String packageName) {
        File[] files = new File(packagePath).listFiles(new FileFilter() {
            @Override
            public boolean accept(File f) {
                return (f.isFile() && f.getName().endsWith(".class")) || f.isDirectory();
            }
        });
        for (File ftemp : files) {
            String filename = ftemp.getName();
            if (ftemp.isFile()) {
                String classname = filename.substring(0, filename.lastIndexOf("."));
                if (StrUtil.isNotBlank(packageName)) {
                    classname = packageName + "." + classname;
                }
                addclass(set, classname);
            } else {
                String subpackagePath = filename;
                if (StrUtil.isNotBlank(packagePath)) {
                    subpackagePath = packagePath + "/" + subpackagePath;

                }
                String subpackageName = filename;
                addclass(set, subpackagePath, subpackageName);
            }
        }
    }

    private static void addclass(Set<Class<?>> set, String className) {
        Class<?> cla = loadClass(className);
        set.add(cla);
    }
}
